package com.wewins.fota.service;

import com.alibaba.fastjson.JSONObject;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.wewins.fota.bean.bo.ExFotaLog;
import com.wewins.fota.bean.bo.filter.FilterCond;
import com.wewins.fota.bean.bo.filter.LogFilterCond;
import com.wewins.fota.bean.dto.ExportReq;
import com.wewins.fota.bean.dto.PageReq;
import com.wewins.fota.bean.dto.PageRst;
import com.wewins.fota.bean.po.FotaLog;
import com.wewins.fota.common.constant.RedisConst;
import com.wewins.fota.mapper.FotaLogMapper;
import com.wewins.fota.service.export.ExportModel;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.TransactionIsolationLevel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Slf4j
@Service
public class FotaLogService implements BasicService<FotaLog, FotaLog>, ExportService {
    public static final int TYPE_INSERT = 1;
    public static final int TYPE_UPDATE = 2;
    private final FotaLogMapper mapper;
    private final SqlSessionFactory factory;
    private final String rootPath;
    private final RedisTemplate<String, Object> template;

    @Autowired
    public FotaLogService(FotaLogMapper mapper, SqlSessionFactory sqlSessionFactory, String rootPath, RedisTemplate<String, Object> template) {
        this.mapper = mapper;
        this.factory = sqlSessionFactory;
        this.rootPath = rootPath;
        this.template = template;
    }

    @Override
    public <K extends FilterCond> PageRst<FotaLog> queryAll(PageReq<K> pageReq) {
        PageHelper.startPage(pageReq);
        return PageRst.<FotaLog>builder().build().fillBy(PageInfo.of(mapper.queryAll(pageReq.getFilterBy())));
    }

    @Override
    public FotaLog findById(long id) {
        return mapper.findById(id);
    }

    @Override
    public FotaLog insert(String username, FotaLog data) {
        mapper.insert(data);
        return data;
    }

    @Override
    public Integer update(String username, FotaLog data) {
        return mapper.updateByWholeQuantity(data);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Integer deleteById(String username, long id) {
        FotaLog fotaLog = this.findById(id);
        if (fotaLog == null) {
            return 0;
        }
        return mapper.delete(fotaLog);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Integer delete(String username, FotaLog data) {
        return this.deleteById(username, data.getId());
    }

    public boolean existLog(FotaLog fotaLog) {
        return mapper.existLog(fotaLog) >= 1;
    }

    public void batchInsertOrUpdate(Map<String, ExFotaLog> batchMap) {
        SqlSession session = factory.openSession(ExecutorType.BATCH, TransactionIsolationLevel.REPEATABLE_READ);
        FotaLogMapper mapper = session.getMapper(FotaLogMapper.class);
        try {
            batchMap.forEach((key, value) -> {
                FotaLog fotaLog = value.getFotaLog();
                switch (value.getDbType()) {
                    case TYPE_INSERT:
                        mapper.insert(fotaLog);
                        break;
                    case TYPE_UPDATE:
                        mapper.updateByIncrement(fotaLog);
                        break;
                    default:
                        break;
                }
            });
            session.commit();
            session.clearCache();
        } catch (Exception e) {
            log.error("failed to batch insert or update fota log, err:", e);
            session.rollback();
            List<String> list = batchMap.values().stream().map(value -> JSONObject.toJSONString(value.getFotaLog())).collect(Collectors.toList());
            this.template.opsForList().rightPushAll(RedisConst.REDIS_KEY_LOG_LIST, list);
        } finally {
            session.close();
        }
    }

    public boolean downloadSuccess(FotaLog fotaLog) {
        return mapper.hasUpdated(fotaLog.getProduct(), fotaLog.getImei(), fotaLog.getToV()) > 0;
    }

    public File export(ExportReq<LogFilterCond> cond) throws IOException {
        PageReq<LogFilterCond> pageCond = PageReq.<LogFilterCond>builder().pageNum(1).pageSize(500).filterBy(cond.getFilter()).orderBy("create_time desc").build();
        SimpleDateFormat format = new SimpleDateFormat("yyyyMMddhhmmssSSS");
        String filePath = rootPath + File.separator + "runtime" + File.separator + "EXPORT_" + format.format(System.currentTimeMillis()) + ".xlsx";
        ExportModel model = ExportModel.getExportModel(cond.getType(), filePath, this);
        while (true) {
            PageRst<FotaLog> pageRst = this.queryAll(pageCond);
            model.fill(pageRst.getData()).write();
            if (pageRst.getPageNum() >= pageRst.getTotalPages()) {
                break;
            }
            pageCond.setPageNum(pageCond.getPageNum() + 1);
        }
        return new File(filePath);
    }

}
